package kim.bat.utils.mvi

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

/**
 * MVIModel
 * 流转路径：Intent 到 Action 到 Plan
 * Plan 同步结果进入 State 变更
 * Plan 异步副作用交给 Actor 产出 Result
 * Reducer 以不可变方式将 Result 合并为新 State
 * Event 从 Result 派生为一次性事件
 * UI 只调用 dispatch 只订阅 stateFlow 和 eventsFlow
 */
abstract class MVIModel<I : IUiIntent, A : IUiAction, E : IUiEffect, R : IUiResult, S : IUiState, V : IUiEvent>(
    initialState: S,
    private val intentMapper: ((I) -> A)? = null
) : ViewModel() {
    protected abstract val reducer: IUiReducer<S, R>
    protected abstract val actor: IUiActor<E, R>

    private val scope: CoroutineScope = viewModelScope

    // 持久状态
    private val _state = MutableStateFlow(initialState)
    val stateFlow: StateFlow<S> = _state.asStateFlow()

    // 一次性事件
    private val _events = MutableSharedFlow<V>(
        replay = 0,
        extraBufferCapacity = 64,
        onBufferOverflow = BufferOverflow.SUSPEND
    )
    val eventsFlow: SharedFlow<V> = _events.asSharedFlow()

    // 计划：同步结果 + 异步副作用 + 并发策略
    protected data class Plan<R, E>(
        val immediateResults: List<R> = emptyList(),
        val effects: List<E> = emptyList(),
        val policy: EffectPolicy = EffectPolicy.Parallel
    )

    // 并发键控（最简实现）
    private val keyedJobs = mutableMapOf<Any, Job>()

    // 严格 FIFO 队列 每个键维护一个顺序队列
    private val keyedQueues = mutableMapOf<Any, ArrayDeque<E>>()

    /** 外部唯一入口：Intent → Action → Plan → 执行 */
    fun dispatch(intent: I) {
        val action = toAction(intent)
        handle(action)
    }

    /** Intent → Action：做归并、参数整形、去重策略选择的入口 */
    protected open fun toAction(intent: I): A =
        intentMapper?.invoke(intent)
            ?: (intent as? A
                ?: error("MVIModel需要intentMapper：当 Intent 与 Action 不同类型时必须提供映射"))

    /** Action → Plan：决定同步 Result 与异步 Effect */
    protected abstract fun plan(action: A): Plan<R, E>

    /** Result → Event（一次性 UI 事件派生） */
    protected open fun eventOf(result: R): V? = null

    /** Effect 键控（用于 Replace/Queue 策略），默认按类型键控 */
    protected open fun effectKey(effect: E): Any = effect::class

    /** 执行流程
     * 先应用同步结果
     * 再按策略执行副作用
     */
    private fun handle(action: A) {
        val p = plan(action)

        // 先应用同步结果（纯计算）
        p.immediateResults.forEach { applyResult(it) }

        // 再按策略执行副作用
        p.effects.forEach { e ->
            when (p.policy) {
                EffectPolicy.Parallel -> launchEffect(null, e, replace = false, queue = false)
                EffectPolicy.ReplaceByKey -> launchEffect(
                    effectKey(e),
                    e,
                    replace = true,
                    queue = false
                )

                EffectPolicy.QueueByKey -> launchEffect(
                    effectKey(e),
                    e,
                    replace = false,
                    queue = true
                )
            }
        }
    }

    /**
     * 启动副作用
     * 无键并行直接启动
     * ReplaceByKey 取消同键的活跃 Job 后启动 并清空该键队列
     * QueueByKey 严格 FIFO 同键按队列顺序执行
     */
    private fun launchEffect(key: Any?, effect: E, replace: Boolean, queue: Boolean) {
        val runEffect: suspend (E) -> Unit = { e ->
            actor.execute(e).collect { applyResult(it) }
        }
        if (key == null) {
            scope.launch { runEffect(effect) }
            return
        }
        val existing = keyedJobs[key]

        if (replace) {
            if (existing?.isActive == true) existing.cancel()
            // 清空可能存在的队列
            keyedQueues.remove(key)
            val job = scope.launch { runEffect(effect) }
            keyedJobs[key] = job
            job.invokeOnCompletion { scope.launch { if (keyedJobs[key] === job) keyedJobs.remove(key) } }
            return
        }

        if (queue) {
            val q = keyedQueues.getOrPut(key) { ArrayDeque() }
            if (existing?.isActive == true) {
                q.addLast(effect)
                return
            }
            // 无活跃任务 直接启动 并在完成后启动下一个
            val job = scope.launch { runEffect(effect) }
            keyedJobs[key] = job
            job.invokeOnCompletion { scope.launch { startNextQueuedEffect(key) } }
            return
        }

        // 默认并行 有键但不使用队列策略 直接启动
        val job = scope.launch { runEffect(effect) }
        keyedJobs[key] = job
        job.invokeOnCompletion { scope.launch { if (keyedJobs[key] === job) keyedJobs.remove(key) } }
    }

    // 从队列启动下一个任务 严格 FIFO
    private fun startNextQueuedEffect(key: Any) {
        val q = keyedQueues[key] ?: return
        val next = q.removeFirstOrNull()
        if (next == null) {
            keyedQueues.remove(key)
            val current = keyedJobs[key]
            if (current?.isActive != true) {
                keyedJobs.remove(key)
            }
            return
        }
        val job = scope.launch {
            actor.execute(next).collect { applyResult(it) }
        }
        keyedJobs[key] = job
        job.invokeOnCompletion { scope.launch { startNextQueuedEffect(key) } }
    }

    /**
     * 应用 Result
     * 通过 Reducer 产生新 State
     * 如需要派生一次性事件 则发出 Event
     */
    private fun applyResult(r: R) {
        _state.update { s -> reducer.reduce(s, r) }
        eventOf(r)?.let { ev ->
            if (!_events.tryEmit(ev)) scope.launch { _events.emit(ev) }
        }
    }

    /** 便利订阅 使用内部作用域 */
    fun <R2> onState(
        map: suspend S.() -> R2,
        isRepeat: Boolean = false,
        block: R2.() -> Unit
    ): Job = scope.launch {
        var f = stateFlow.map(map)
        if (!isRepeat) f = f.distinctUntilChanged()
        f.collect(block)
    }

    /** 便利订阅 使用内部作用域 */
    fun onEvent(block: V.() -> Unit): Job =
        scope.launch { eventsFlow.collect(block) }
}

abstract class SimpleMVIModel<I : IUiIntentAction, E : IUiEffect, R : IUiResult, S : IUiState>(
    initialState: S
) : MVIModel<I, I, E, R, S, EmptyEvent>(initialState = initialState)

abstract class EventMVIModel<I : IUiIntentAction, E : IUiEffect, R : IUiResult, S : IUiState, V : IUiEvent>(
    initialState: S
) : MVIModel<I, I, E, R, S, V>(initialState = initialState)